from typing import Any

from vnpy_ctastrategy import (
    CtaTemplate,
    StopOrder,
    TickData,
    BarData,
    TradeData,
    OrderData,
    BarGenerator,
    ArrayManager,
)


class BollDemoPercentSlStrategy(CtaTemplate):
    """
    布林线demo策略-可变止损
    """
    author = "Cjie Wong"

    boll_window = 18
    boll_dev = 3.4
    fixed_size = 1
    # percent stop loss , 可变止损
    percent_sl = 0.01

    boll_up = 0
    boll_down = 0
    boll_mid = 0

    long_entry = 0
    long_sl = 0
    short_entry = 0
    short_sl = 0

    parameters = [
        "boll_window", "boll_dev", "fixed_size", "percent_sl"
    ]

    variables = [
        "boll_up", "boll_down", "boll_mid",
        "long_entry", "long_sl",
        "short_entry", "short_sl"
    ]

    def __init__(self, cta_engine: Any, strategy_name: str, vt_symbol: str, setting: dict) -> None:
        super().__init__(cta_engine, strategy_name, vt_symbol, setting)
        self.bg = BarGenerator(self.on_bar, 15, self.on_15min_bar)
        self.am = ArrayManager()

    def on_init(self) -> None:
        """
        Callback when strategy is inited
        """
        self.write_log("策略初始化,载入10天的历史数据")
        self.load_bar(10)

    def on_start(self) -> None:
        """
        Callback when strategy is started
        """
        self.write_log("策略启动")

    def on_stop(self) -> None:
        """
        Callback when strategy is stopped
        """
        self.write_log("策略停止")

    def on_tick(self, tick: TickData) -> None:
        """
        Callback of new tick data update.
        """
        self.bg.update_tick(tick)

    def on_bar(self, bar: BarData) -> None:
        """
        Callback of new bar data update.
        """
        self.bg.update_bar(bar)

    def on_15min_bar(self, bar: BarData):
        """"""
        self.cancel_all()
        am = self.am
        am.update_bar(bar)
        if not am.inited:
            return
        # 计算此刻布林线的上线和下线对应的数值
        self.boll_up, self.boll_down = am.boll(self.boll_window, self.boll_dev)
        # 计算此刻布林线的中线对应的数值，即简单移动平均线的值
        self.boll_mid = am.sma(self.boll_window)
        # 如果没有持仓
        if (self.pos == 0):
            # 以布林线上线的值作为价格，发起一个买入的停止单（若此刻市场价高于这个值，会立即触发以市价成交）
            self.buy(self.boll_up, self.fixed_size, True)
            # 以布林线下线的值作为价格，发起一个卖出的停止单（若此刻市场价低于这个值，会立即触发以市价成交）
            self.short(self.boll_down, self.fixed_size, True)

            self.long_entry = self.boll_up
            self.short_entry = self.boll_down
        # 如果持有多头仓位
        elif self.pos > 0:
            # 如果K线的收盘价向下突破了布林线中线
            if(bar.close_price <= self.boll_mid):
                # 立即以优势价格（收盘价-5元）卖出平仓
                self.sell(bar.close_price - 5, abs(self.pos))
            # 以long_entry * (1-percent_sl)价格设置多头止损价（stop loss）
            self.long_sl = self.long_entry * (1-self.percent_sl)
            # 创建一个卖出停止单，价格只要跌破long_sl就触发市价卖出
            self.sell(self.long_sl, abs(self.pos), True)
        # 如果持有空头仓位
        elif self.pos < 0:
            # 如果K线的收盘价突破布林线中线
            if(bar.close_price >= self.boll_mid):
                # 立即以优势价格（收盘价+5元）买入平仓
                self.cover(bar.close_price + 5, abs(self.pos))
            # 以short_entry * (1+percent_sl)的价格设置空头止损价（stop loss）
            self.short_sl = self.short_entry * (1+self.percent_sl)
            # 创建一个停止单，价格只要突破short_sl就会触发市价买入平仓
            self.cover(self.short_sl, abs(self.pos), True)

        self.put_event()

    def on_trade(self, trade: TradeData) -> None:
        """
        Callback of new trade data update.
        """
        pass

    def on_order(self, order: OrderData) -> None:
        """
        Callback of new order data update
        """
        pass

    def on_stop_order(self, stop_order: StopOrder) -> None:
        """
        Callback of stop order update
        """
        pass
